package org.jeecg;

import net.bytebuddy.implementation.bytecode.Throw;
import org.junit.runner.RunWith;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.test.context.junit4.SpringRunner;

import java.io.File;
import java.io.FileReader;
import java.io.FileWriter;
import java.io.IOException;
import java.util.*;

/**
 * json转为实体类（包括赋值）
 *
 * @author liujiamiao
 * @email 2544296058@qq.com
 * @date 2020-10-27 14:54
 */
@RunWith(SpringRunner.class)
@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT)
public class BasicJsonGenerate {

    private static String clazzName = null;

    private static String packageName = null;

    private static String path = null;

    public static void main(String[] args) throws IOException {
        String jsonName = "GenerateJson.json";

        //包名
        packageName = "com.chunuo.generate";
        //路径
        path = "D:\\JAVA\\poi\\";
        //读取文件内容
        String xml = read(path, jsonName);
        //获取java文件名
        clazzName = jsonName.substring(0,jsonName.indexOf("."));

        //构建java文本
        String java = build(xml);
        System.out.println(java);

        //写入到java类中
        write(path+(packageName.replace('.','\\')),clazzName+".java",java.toString());

    }


    /**
     * 构建java类内容
     * @param xml
     * @return
     */
    private static String build(String xml) {
        Map<String,Object> map = new HashMap<>();
        //java类的内容，拼完串就写到文件里面
        StringBuilder java = new StringBuilder();
        List<String> list = new ArrayList<>();

        int left = xml.indexOf("{");
        int right = xml.lastIndexOf("}");
        if(left!=-1&&right!=-1&&left<right){
            //初始代码
            java.append("package "+packageName+";\r\n\r\n" +
                    "import java.util.*;\r\n\r\n/**\n" +
                    " * 类的作用\n" +
                    " * @author liujiamiao\n" +
                    " * @email 2544296058@qq.com\n" +
                    " */\r\n" +
                    "public class "+clazzName+" {\r\n");
            //获取所有属性
            String sub = xml.substring(left + 1, right).trim();
            //拆分为多个属性
//            String[] fields = sub.split(",");
            //由于字段中有集合或map属性，则会拆分混乱，所以需要自己写一个split方法
            List<String> fields = split(sub);

            for(String field : fields){
                int leftObj = -1;
                if(field.indexOf('[')!=-1&&field.indexOf(']')!=-1){
                    //属性拆分为key：value
                    int index = field.indexOf(':');
                    //属性名
                    String key = subString(field.substring(0,index).trim(),true);
                    //属性类型
                    String fieldType = "Object";
                    //集合的值
                    String temp = field.substring(index+1);
                    if(object(temp)){
                        //含有多种类型，泛型为Object
                    }else if(temp.indexOf('"')!=-1){
                        fieldType = "String";
                    }else if(temp.indexOf('.')!=-1){
                        fieldType = "Float";
                    }else if(temp.indexOf('{')!=-1&&temp.indexOf('}')!=-1){
                        throw new RuntimeException("该版本不进行对象封装，如果需要使用对象封装，请使用第二版本！" +
                                "原因是：该版本将json转为java类并将属性赋值，不常用。" +
                                "第二版本将会生成json对应的实体类对象。");
                        //说明是对象
//                        if(key.charAt(key.length()-1)=='s'){
//                            fieldType = key.substring(0,key.length()-1);
//                        }else if(key.indexOf("List")!=-1){
//                            fieldType = key.substring(0,key.indexOf("List"));
//                        }else{
//                            System.out.println("属性名不规范，无法判断其类名，默认为Object，集合命名格式为：xxxList | xxxs");
//                        }
                    }else{
                        fieldType = "Integer";
                    }
                    //属性名和属性值
                    map.put(key,"new ArrayList<"+fieldType+">()");
                    //两边去掉空格，再截取出[里面的内容以","为拆分点]
                    String[] vals = temp.substring(1, temp.length() - 1).split(",");
                    StringBuilder sb = new StringBuilder();
                    for(String val : vals){
                        val = val.trim();
                        if(fieldType.equals("String")){
                            if(val.charAt(0)=='"'&&val.charAt(val.length()-1)=='"'){
                                sb.append("\t\t"+key+".add("+val+");\r\n");
                            }else{
                                //上面已将含有多种格式的集合封装为object泛型，因此，该异常报废
                                throw new RuntimeException("String类型，封装了非字符串值 ："+Arrays.asList(vals)+" == > "+val);
                            }
                        }else if(fieldType.equals("Object")||
                                fieldType.equals("Boolean")||
                                fieldType.equals("Integer")||
                                fieldType.equals("Float")||
                                fieldType.equals("Double")){
                            sb.append("\t\t"+key+".add("+val+");\r\n");
                        }else{
                            //对象的数组类型
                        }
                    }
                    list.add(sb.toString());
                }

                else if(field.indexOf('{')!=-1&&field.indexOf('}')!=-1){
                    //map属性
                    //属性拆分为key：value
                    int index = field.indexOf(':');
                    //属性名和属性值
                    String key = subString(field.substring(0,index).trim(),true);
                    //属性名和属性值
                    map.put(key,"new HashMap()");
                    //截取出该属性的value部分
                    String temp = field.substring(index + 1).trim();
                    //两边去掉空格，再截取出{里面的内容以","为拆分点}
                    String[] split = temp.substring(1, temp.length() - 1).split(",");
                    StringBuilder sb = new StringBuilder();
                    for(String s:split){
                        String[] obj = s.split(":");
                        if(obj.length>2){
                            throw new RuntimeException("json格式错误！请检查是否忽略\",\" ："+s);
                        }
                        sb.append("\t\t"+key+".put(\""+obj[0].trim()+"\","+obj[1].trim()+");\r\n");
                    }
                    list.add(sb.toString());
                } else{
                    //不是map和list属性，不可能有多个":"
                    String[] obj = field.split(":");
                    if(obj.length>2){
                        throw new RuntimeException("json格式错误！请检查是否忽略\",\" ："+field);
                    }
                    //属性名和属性值
                    map.put(subString(obj[0].trim(),true),subString(obj[1].trim(),false));
                }


            }

            Set<Map.Entry<String, Object>> entrySet = map.entrySet();
            //遍历所有属性
            for(Map.Entry entry:entrySet){
                String type = getType(entry.getValue());
                java.append("\tprivate "+type+" "+entry.getKey()+" = "+entry.getValue()+(type.equalsIgnoreCase("Float")?"F":"")+";\r\n");
            }
            //构造方法
            java.append("\tpublic "+clazzName+"() {\r\n");

            //构造方法的初始化代码
            for(String sb : list){
                java.append(sb);
            }
            //结束
            java.append("\t}\r\n}");

            return java.toString();
        }else{
            throw new RuntimeException("json格式不正确！");
        }
    }

    /**
     * 判断集合是否含有多种数据类型
     * @param temp
     * @return
     */
    private static boolean object(String temp) {
        System.out.println(temp);
        if(temp.indexOf("{")!=-1)
            return false;//不封装对象类型
        String val = temp.substring(1,temp.length()-1);
        String[] fields = val.split(",");
        int a = 0,b = 0,c = 0;
        for(String field : fields){
            if(field.trim().indexOf('"')!=-1){
                a++;//字符串
            }else if(field.trim().indexOf('.')!=-1){
                b++;//精度形
            }else{
                c++;//整形
            }
        }
        //只有一种数据类型
        if((a>b?(a>c?a:c):(b>c?b:c))==fields.length){
            return false;
        }
        //含有多种数据类型
        return true;
    }

    /**
     * 对象根据","拆分属性
     * @param sub
     * @return
     */
    private static List<String> split(String sub) {
        List<String> list = new ArrayList<String>();
        int a = 0;//表示数组
        int o = 0;//表示map对象属性
        int index = -1;
        for(int i = 0;i<sub.length();i++){
            if(sub.charAt(i)=='['){
                a++;
            }else if(sub.charAt(i)==']'){
                a--;
            }else if(sub.charAt(i)=='{'){
                o++;
            }else if(sub.charAt(i)=='}'){
                o--;
            }else if(sub.charAt(i)==','&&a==0&&o==0){
                list.add(sub.substring(index+1,i));
                index = i;
            }
        }
        if(sub.charAt(sub.length()-1)!=','){
            list.add(sub.substring(index+1));
        }
        return  list;
    }

    /**
     * 根据value获取属性类型
     * @param value
     * @return
     */
    private static String getType(Object value) {
        int len = 0;
        if(value.toString().indexOf('"')!=-1){
            return "String";
        }else if((len = value.toString().indexOf('.'))!=-1){
            //精度小于6的是float类型，否则是double类型
            if(value.toString().length()-len<=6){
                try {
                    Float.parseFloat(value.toString());
                    return "Float";
                } catch (NumberFormatException e) {
                    throw new RuntimeException("类型错误，既不是字符串，又不是精度类型 ："+value.toString());
                }
            }else{
                try {
                    Double.parseDouble(value.toString());
                    return "Double";
                } catch (NumberFormatException e) {
                    throw new RuntimeException("类型错误，既不是字符串，又不是精度类型 ："+value.toString());
                }
            }
        }else if(value.toString().equals("true")||value.toString().equals("false")){
            return "Boolean";
        }else if(value.toString().indexOf("new ArrayList")!=-1){
            //判断list属性类型
            int i = value.toString().indexOf('<');
            int j = value.toString().indexOf('>');
            return "List<"+value.toString().substring(i+1,j)+">";
        }else if(value.toString().indexOf("new HashMap")!=-1){
            //判断map属性类型
            //准备更改为对象类型，而不是map
            return "Map";
        }else {
            return "Integer";
        }
    }

    /**
     * 截取key/value的值，去掉""
     * @param field
     * @return
     */
    public static String subString(String field,boolean isKey){
        if(field.indexOf('"')!=-1){
            //说明字段用了""围起来
            int i = field.indexOf('"');
            int j = field.lastIndexOf('"');
            if(i!=-1&&j!=-1&&i<j){
                if(isKey){
                    //说明是key，需要去掉"";
                    return field.substring(i+1,j);
                }else{
                    //说明是value，是字符串
                    return field;
                }

            }else{
                throw new RuntimeException("json格式不正确！field只有一个\" ："+field);
            }
        }else{
            //没有用""围起来，可能是key或者不是字符串
            if(field.indexOf(" ")!=-1){
                throw new RuntimeException("json语法不正确！field含有空格："+field);
            }
            return field;
        }
    }

    /**
     * 读取文件内容
     * @param path
     * @param fileName
     * @return
     * @throws IOException
     */
    public static String read(String path ,String fileName) throws IOException {
        //读取
        FileReader reader = new FileReader(new File(path,fileName));
        char[] buf = new char[50];
        int len = -1;
        StringBuilder sb = new StringBuilder();
        while((len = reader.read(buf))!=-1){
            sb.append(new String(buf,0,len));
        }
        reader.close();
        return sb.toString();
    }

    /**
     * 写入文件
     * @param path
     * @param fileName
     * @param content
     * @throws IOException
     */
    public static void write(String path,String fileName,String content) throws IOException {
        System.out.println(path);
        File filePath = new File(path);
        if(!filePath.exists()){
            filePath.mkdirs();
        }
        File file = new File(path, fileName);
        FileWriter writer = new FileWriter(file);
        writer.write(content);
        writer.flush();
        writer.close();
    }

    public static void show(List list){
        for(int i = 0;i<list.size();i++){
            System.out.println(list.get(i));
        }
    }
}
